﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using mCore;

namespace SMS
{
    // The class to hold the information about the event
    // in this case it will hold only information
    // available in the clock class, but could hold
    // additional state information
    public enum MODEM_STATE
    {
        DISCONNECTED = 0,
        CONNECTING,
        CONNECTED,
        READY,
        ERROR_SIM_OUT_OF_MONEY,
        ERROR
    }
    public class SMSSentEventArgs : EventArgs
    {
		#region Fields (5) 

        public readonly string Description;
        public readonly string DestinationNumber;
        public readonly long ErrorCode;
        public readonly int ID;
        public readonly bool SendResult;

		#endregion Fields 

		#region Constructors (1) 

        public SMSSentEventArgs(int iID, string sDestinationNumber, long sErrorCode, string sDescription, bool bSendResult)
        {
            this.ID = iID;
            this.DestinationNumber = sDestinationNumber;
            this.ErrorCode = sErrorCode;
            this.Description = sDescription;
            this.SendResult = bSendResult;
        }

		#endregion Constructors 
    }

    public class SMSSendingEventArgs : EventArgs
    {
		#region Fields (2) 

        public readonly string DestinationNumber;
        public readonly int ID;

		#endregion Fields 

		#region Constructors (1) 

        public SMSSendingEventArgs(int iID,string sDestinationNumber)
        {
            this.ID = iID;
            this.DestinationNumber = sDestinationNumber;
        }

		#endregion Constructors 
    }

    class GsmModem : ListViewItem ,IEquatable<GsmModem>
    {
		#region Fields (17) 

        public int FailedCounter = 0;
        public string IMEI = "";
        private bool isSending = false;
        public string Manufacture = "";
        private string mPortName;
        private mCore.SMS objSMS = new mCore.SMS();
        //Thread sendingThread = null;
        public int SentCounter = 0;
        public ListViewItem.ListViewSubItem siFailed;
        ListViewItem.ListViewSubItem siName;
        public ListViewItem.ListViewSubItem siSent;
        public ListViewItem.ListViewSubItem siSentTotal;
        ListViewItem.ListViewSubItem siSignal;
        ListViewItem.ListViewSubItem siStatus;
        public List<SMSContent> smsList = new List<SMSContent>();
        public  Dictionary<string, int> smsMap = new Dictionary<string, int>();

        public MODEM_STATE mState;
		#endregion Fields 

		#region Constructors (3) 

        public GsmModem(string portname)
        {
            mState = MODEM_STATE.DISCONNECTED;
            mPortName = portname;
            mCore.License objLic = objSMS.License();
            objLic.Company = "The CompanyX Inc.";
            objLic.LicenseType = "PRO-DISTRIBUTION";
            objLic.Key = "AE1K-X12R-GHEK-JEWS";
            SetCommParameters(mPortName);
            objSMS.Encoding = mCore.Encoding.GSM_Default_7Bit;
            objSMS.LongMessage = mCore.LongMessage.Concatenate;
            objSMS.Queue().Enabled = true;
            /* init subitems */
            // port name
            siName = new ListViewItem.ListViewSubItem(this, portname);
            if (siName != null)
                this.SubItems.Add(siName);
            // modem status
            siStatus = new ListViewItem.ListViewSubItem(this, "Ngắt kết nối");
            if (siStatus != null)
                this.SubItems.Add(siStatus);
            // signal length
            siSignal = new ListViewItem.ListViewSubItem(this, "0");
            if (siSignal != null)
                this.SubItems.Add(siSignal);
            // sent
            siSentTotal = new ListViewItem.ListViewSubItem(this, (SentCounter + FailedCounter).ToString());
            if (siSentTotal != null)
                this.SubItems.Add(siSentTotal);

            siSent = new ListViewItem.ListViewSubItem(this, SentCounter.ToString());
            if (siSent != null)
                this.SubItems.Add(siSent);

            siFailed = new ListViewItem.ListViewSubItem(this, FailedCounter.ToString());
            if (siFailed != null)
                this.SubItems.Add(siFailed);

            objSMS.QueueSMSSending += new mCore.SMS.QueueSMSSendingEventHandler(objSMS_QueueSMSSending);
            objSMS.QueueSMSSent += new mCore.SMS.QueueSMSSentEventHandler(objSMS_QueueSMSSent);
            objSMS.QueueWapPushSending += new mCore.SMS.QueueWapPushSendingEventHandler(objSMS_QueueWapPushSending);
            objSMS.QueueWapPushSent += new mCore.SMS.QueueWapPushSentEventHandler(objSMS_QueueWapPushSent);
            /*
            sendingThread = (new System.Threading.Thread(() =>
            {
                try
                {
                    // remove all sms in list
                    while(smsList.Count > 0)
                    {
                        if (isCancel) break;
                        SMSContent content = smsList[0];
                        if (content == null)
                            continue;
                        // sending the sms
                        // temporary disable for GUI test
                        sendMessage(content);
                        // raise seding sms event
                        //OnSendingSMS(this, new SMSSendingEventArgs(content.ID, content.Destination));
                        // remove first item
                        smsList.RemoveAt(0);
                        // sleep
                        System.Threading.Thread.Sleep(1000);
                    }
                }
                catch (ThreadAbortException ThreadAbort)
                {
                    //...
                }
                catch (ThreadInterruptedException ThreadInterrupt)
                {
                    //...
                }
                finally
                {

                }
            })
            {
                Name = "Sending sms"
            }
               );
            */
        }

        public GsmModem()
        {
            /*
            sendingThread = (new System.Threading.Thread(() =>
            {
                try
                {
                    //foreach (SMSContent content in smsList)
                    for (int i = 0; i < smsList.Count; i++)
                    {
                        SMSContent content = smsList[i];
                        if (isCancel) break;
                        sendMessage(content);

                        //OnSendingSMS(this, new SMSSendingEventArgs(content.ID, content.Destination));
                        smsList.RemoveAt(i);
                        System.Threading.Thread.Sleep(200);
                    }
                }
                catch (ThreadAbortException ThreadAbort)
                {
                    //...
                }
                catch (ThreadInterruptedException ThreadInterrupt)
        {
                    //...
        }
                finally
                {

                }
            })
        {
                Name = "Sending sms"
            }
               );*/
            mState = MODEM_STATE.DISCONNECTED;
        }

        ~GsmModem()
        {
            if (objSMS.IsConnected)
            {
                objSMS.Disconnect();
            }
        }

		#endregion Constructors 

		#region Properties (1) 

        public string Portname
        {
            get { return mPortName; }
            set { mPortName = value; }
        }

		#endregion Properties 

		#region Delegates and Events (4) 

		// Delegates (2) 

        // A delegate type for hooking up change notifications.
        public delegate void SMSSendingEventHandler(object sender, SMSSendingEventArgs e);
        public delegate void SMSSentEventHandler(object sender, SMSSentEventArgs e);
		// Events (2) 

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event SMSSendingEventHandler SendingSMS;

        public event SMSSentEventHandler SentSMS;

		#endregion Delegates and Events 

		#region Methods (21) 

		// Public Methods (9) 

        public bool connect()
        {
            ChangeState(MODEM_STATE.CONNECTING);

            SetCommParameters(mPortName);
 
            try
            {
                if (objSMS.IsConnected)
                    objSMS.Disconnect();
                objSMS.Connect();
                objSMS.Encoding = mCore.Encoding.GSM_Default_7Bit;
                objSMS.LongMessage = mCore.LongMessage.Concatenate;
                objSMS.Queue().Enabled = true;
                if (objSMS.IsConnected)
                {
                    this.Checked = true;
                    mState = MODEM_STATE.CONNECTED;
                    // get manufacture, to check for huawei modems
                    Manufacture = objSMS.Manufacturer;
                    // get imei
                    IMEI = objSMS.IMEI;
                    string name = Manufacture + " - " + objSMS.Model;
                    //string ownnumber ;//= objSMS.OwnNumber;
                    siStatus.Text = "Đã kết nối - " + name;// = new ListViewItem.ListViewSubItem(this, "Connected");
                    siSignal.Text = objSMS.SignalStrength.ToString();

                    mState = MODEM_STATE.READY;
                    return true;
                }
                else
                {
                    ChangeState(MODEM_STATE.DISCONNECTED);
                    return false;
                }
            }
            catch (mCore.GeneralException e)
            {
                ChangeState(MODEM_STATE.ERROR);
                Console.WriteLine(e.Message);
                return false;
            }
            catch (Exception e)
            {
                ChangeState(MODEM_STATE.ERROR);
                Console.WriteLine(e.Message);
            }
            return false;
        }

        public bool Equals(GsmModem other)
        {
            if (this.Portname == other.Portname)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void sendMessage(SMSContent sms)
        {
            if (sms.URL == "")
                sendTextMessage(sms);
            else
                sendWapPushMessage(sms);
        }

        public void sendMessageList()
        {
            // we only send sms when modem is ready
            if (mState != MODEM_STATE.READY) return;
            if (smsList.Count > 0)
                isSending = true;
            else
            {
                isSending = false;
                return;
            }
            SMSContent content = smsList[0];
            if (content == null)
                return;
            // send the sms
            sendMessage(content);
            // remove the first item
            if (smsList.Count > 0)
                smsList.RemoveAt(0);
            /*
            sendingThread = (new System.Threading.Thread(() =>
            {
                try
                {
                    SMSContent content = smsList[0];
                    if (content == null)
                        return ;
                    sendMessage(content);
                    /*
                    // remove all sms in list
                    while (smsList.Count > 0)
                    {
                        //if (isCancel) break;
                        SMSContent content = smsList[0];
                        if (content == null)
                            continue;
                        // sending the sms
                        // temporary disable for GUI test
                        sendMessage(content);
                        // raise seding sms event for testing
                        //OnSendingSMS(this, new SMSSendingEventArgs(content.ID, content.Destination));
                        // remove first item
                        if (smsList.Count > 0)
                            smsList.RemoveAt(0);
                        // sleep
                        System.Threading.Thread.Sleep(300);
                    }
                     
                }
                catch (ThreadAbortException ThreadAbort)
                {
                    //...
                }
                catch (ThreadInterruptedException ThreadInterrupt)
                {
                    //...
                }
                finally
                {

                }
            })
            {
                Name = "Sending sms"
            }
               );
                sendingThread.Start();
            */
        }

        public void stopSending()
        {
            //isCancel = true;
            isSending = false;
            //sendingThread.Abort();
        }

        public void updateSMSInfo()
        {
            this.siSent.Text = this.SentCounter.ToString();
            this.siFailed.Text = this.FailedCounter.ToString();
            this.siSentTotal.Text = (this.SentCounter + this.FailedCounter).ToString();
        }

        //Event at the end of sending a queued message
        public void updateSMSInfo(mCore.QueueSMSSentEventArgs e)
        {
            if (e.ErrorCode > 0)
            {
                FailedCounter++;
                //Console.WriteLine("[ERROR: " + e.ErrorCode + "] sent queuesms: " + e.ErrorDescription);
                //this.siFailed.Text = FailedCounter.ToString();
            }
            else
            {
                SentCounter++;
                //this.siSentTotal.Text = SentCounter.ToString();
            }
        }
		// Protected Methods (2) 

        // By Default, create an OnXXXX Method, to call the Event
        protected void OnSendingSMS(object sender, SMSSendingEventArgs e)
        {
            if (SendingSMS != null)
            {
                SendingSMS(sender,e);
            }
        }

        // By Default, create an OnXXXX Method, to call the Event
        protected void OnSentSMS(object sender, SMSSentEventArgs e)
        {
            if (SentSMS != null)
            {
                SentSMS(sender, e);
            }
        }
		// Private Methods (9) 

        private int getSMSIdWithKey(string key)
        {
            // See whether Dictionary contains this string.
            if (smsMap.ContainsKey(key))
            {
                int value = smsMap[key];
                return value;
            }
            return -1;
        }

        //Event at the start of sending a queued message
        private void objSMS_QueueSMSSending
           (object sender, mCore.QueueSMSSendingEventArgs e)
        {
            Console.WriteLine("Sending message to: " + e.DestinationNumber + "/r/n" +
                  "[Queue Key: " + e.QueueMessageKey + "]");
            int smsID = getSMSIdWithKey(e.QueueMessageKey);
            if (smsID > -1)
                OnSendingSMS(sender, new SMSSendingEventArgs(smsID,e.DestinationNumber));
            else
                Console.WriteLine("GsmModem: Can not find sms ID");
        }

        //Event when the sms is sent
        private void objSMS_QueueSMSSent
           (object sender, mCore.QueueSMSSentEventArgs e)
        {
            Console.WriteLine("Sent message to: " + e.DestinationNumber + "/r/n" +
                  "[Queue Key: " + e.QueueMessageKey + "]");
            if(e.ErrorCode == 4012 && false)// truong hop sim het tien tam thoi disable ko dung, se update o version sau
            {
                //mState = MODEM_STATE.ERROR;
                
                isSending = false;
                Console.WriteLine(e.ErrorDescription);
            }
            // when the sms is send, we check if we need to send more, send again.
            int smsID = getSMSIdWithKey(e.QueueMessageKey);

            if (smsID > -1)
            {
                //updateSMSInfo(e);
                OnSentSMS(this, new SMSSentEventArgs(smsID, e.DestinationNumber, e.ErrorCode, e.ErrorDescription, e.SendResult));
                if (isSending && smsList.Count > 0)
                    sendMessageList();
                //Thread.Sleep(300);
            }
            else
                Console.WriteLine("GsmModem: Can not find sms ID");
            
        }

        //Event at the start of sending a queued WAP Push message
        private void objSMS_QueueWapPushSending
           (object sender, mCore.QueueWapPushSendingEventArgs e)
        {
            Console.WriteLine("Sending message to: " + e.DestinationNumber + "/r/n" +
                  "[Queue Key: " + e.QueueMessageKey + "]");
            int smsID = getSMSIdWithKey(e.QueueMessageKey);
            if (smsID > -1)
                OnSendingSMS(sender, new SMSSendingEventArgs(smsID, e.DestinationNumber));
            else
                Console.WriteLine("GsmModem: Can not find sms ID");
        }

        //Event at the end of sending a queued WAP Push message
        private void objSMS_QueueWapPushSent
           (object sender, mCore.QueueWapPushSentEventArgs e)
       {
           Console.WriteLine("Sent message to: " + e.DestinationNumber + "/r/n" +
                   "[Queue Key: " + e.QueueMessageKey + "]");
           if (e.ErrorCode == 4012 && false)
           {
               //isSending = false;
               //mState = MODEM_STATE.ERROR;
               
               Console.WriteLine(e.ErrorDescription);
           }
           int smsID = getSMSIdWithKey(e.QueueMessageKey);

           if (smsID > -1)
           {
               //updateSMSInfo(e);
               OnSentSMS(this, new SMSSentEventArgs(smsID, e.DestinationNumber, e.ErrorCode, e.ErrorDescription, e.SendResult));
               if (isSending && smsList.Count > 0)
                   sendMessageList();
           }
           else
               Console.WriteLine("GsmModem: Can not find sms ID");
       }
        public void ChangeState(MODEM_STATE state)
        {
            mState = state;
            switch (state)
            {
                case MODEM_STATE.DISCONNECTED:
                    siStatus.Text = "Ngắt kết nối";
                    break;
                case MODEM_STATE.CONNECTING:
                    siStatus.Text = "Đang kết nối...";
                    break;
                case MODEM_STATE.CONNECTED:
                    siStatus.Text = "Đã kết nôi";
                    break;
                case MODEM_STATE.ERROR_SIM_OUT_OF_MONEY:
                    siStatus.Text = "Sim hết tiền";
                    break;
                case MODEM_STATE.ERROR:
                    siStatus.Text = "Lỗi modem";
                    siSignal.Text = "-1";
                    break;
            }

        }
        private void sendTextMessage(SMSContent sms)
        {
            
            //if (!sms.isValid()) return;//TODO: check sms is valid
            try
            {
                string strKey = objSMS.SendSMSToQueue(sms.Destination, sms.Content);
                smsMap.Add(strKey,sms.ID);
                
            }

            catch (mCore.GeneralException e)
            {
                MessageBox.Show(e.ToString());
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

        private void sendWapPushMessage(SMSContent sms)
        {
            mCore.WapPush objWP = objSMS.WapPush();
            string strKey = "";
            
            try
            {
                //SetCommParameters(mPortName);
                objWP.Destination = sms.Destination;
                objWP.Text = sms.Content;
                objWP.URL = sms.URL;
                objWP.Action = mCore.WapPushAction.SignalMedium;
                //objWP.Created = DateTime.Now;
                //objWP.Expiry = "1W";

                //Message queue is enabled
                //objSMS.Queue().Enabled = true;

                //Messages can be added to the queue even
                //when the Message queue is enabled
                if (objWP.IsLengthOK)
                {
                    strKey = objWP.SendToQueue();
                    smsMap.Add(strKey, sms.ID);
                    //MessageBox.Show("WAP Push message added to queue.\r\n" +
                      //       "[Queue Key: " + strKey + "]");
                }
            }
            catch (mCore.GeneralException e)
            {
                MessageBox.Show(e.ToString());
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

        private void SetCommParameters(string portname)
        {
            try
            {
                objSMS.Port = portname;
                objSMS.BaudRate = mCore.BaudRate.BaudRate_115200;
                objSMS.DataBits = mCore.DataBits.Eight;
                objSMS.StopBits = mCore.StopBits.One;
                objSMS.Parity = mCore.Parity.None;
                //objSMS.FlowControl = mCore.FlowControl.RTS_CTS;// using hardware flow control will send duplicate sms in HUAWEI and NOVATEL modem so disable this, using NONE
                //objSMS.FlowControl = mCore.FlowControl.None;
                
                objSMS.Timeout = 2000;
                //comment
                //objSMS.Connect();
            }
            catch (mCore.GeneralException e)
            {
                Console.WriteLine(e.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
		// Internal Methods (1) 

        internal void disconnect()
        {
            if (objSMS.IsConnected)
            {
                bool result = objSMS.Disconnect();
                ChangeState(MODEM_STATE.DISCONNECTED);
            }
        }

		#endregion Methods 
    }
}
